package cn.cpf.tool.doc.pdf;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import lombok.Cleanup;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionGoTo;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDNamedDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageFitWidthDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineNode;

/**
 * <b>Description : </b>
 *
 * @author CPF
 * Date: 2020/5/7 10:04
 */
public class PDFBoxUtil {
    private static List<BookmarkItem> getBookmarkLevel(PDOutlineNode bknode, int level) throws IOException {
        PDOutlineItem current = bknode.getFirstChild();
        List<BookmarkItem> items = new ArrayList<>();
        while (current != null) {
            BookmarkItem item = new BookmarkItem();
            item.setName(current.getTitle());
            item.setLevel(level);
            if (current.getDestination() instanceof PDPageDestination) {
                PDPageDestination pd = (PDPageDestination)current.getDestination();
                item.setPageNum(pd.retrievePageNumber());
            }
            if (current.getAction() instanceof PDActionGoTo) {
                PDActionGoTo gta = (PDActionGoTo)current.getAction();
                PDDestination dst = gta.getDestination();
                if (dst instanceof PDPageDestination) {
                    PDPageDestination pd = (PDPageDestination)dst;
                    item.setPageNum(pd.retrievePageNumber());
                } else if (dst instanceof PDNamedDestination) {
                }
            }
            items.add(item);
            List<BookmarkItem> bookmarkLevel = getBookmarkLevel(current, level + 1);
            items.addAll(bookmarkLevel);
            current = current.getNextSibling();
        }
        return items;
    }

    public static List<BookmarkItem> getBookmarkFromFile(String strFile) {
        if (StringUtils.isNotBlank(strFile)) {
            try {
                @Cleanup PDDocument document = PDDocument.load(new File(strFile));
                PDDocumentCatalog documentCatalog = document.getDocumentCatalog();
                PDDocumentOutline outline = documentCatalog.getDocumentOutline();
                if (outline != null) {
                    return getBookmarkLevel(outline, 0);
                }
            } catch (IOException ex) {
                Logger.getLogger(PDFBoxUtil.class.getName()).log(Level.SEVERE, (String) null, ex);
            }
        }
        return Collections.emptyList();
    }

    private static PDOutlineItem makeBookmarkItem(BookmarkItem item) {
        PDOutlineItem bookmark = new PDOutlineItem();
        bookmark.setTitle(item.getName());
        if (item.isPageNumOK()) {
            PDPageFitWidthDestination pDPageFitWidthDestination = new PDPageFitWidthDestination();
            pDPageFitWidthDestination.setPageNumber(item.getPageNum());
            bookmark.setDestination((PDDestination)pDPageFitWidthDestination);
        }
        return bookmark;
    }

    private static int saveBookmarkLevel(PDOutlineNode bknode, int level, ArrayList<BookmarkItem> items, int index) throws IOException {
        while (index < items.size()) {
            BookmarkItem item = items.get(index);
            if (level > item.getLevel())
                break;
            if (level == item.getLevel()) {
                bknode.addLast(makeBookmarkItem(item));
                index++;
                continue;
            }
            if (null == bknode.getLastChild()) {
                bknode.addLast(makeBookmarkItem(item));
                index++;
                break;
            }
            index = saveBookmarkLevel((PDOutlineNode)bknode.getLastChild(), level + 1, items, index);
        }
        return index;
    }

    public static boolean saveBookmarkToFile(String strFile, ArrayList<BookmarkItem> items, int basePage) throws Exception {
        boolean bOK = false;
        if (strFile.isEmpty())
            throw new Exception("Error: File Path is empty." + strFile);
        PDDocument document = null;
        try {
            document = PDDocument.load(new File(strFile));
            if (document.isEncrypted())
                throw new Exception("Error: can not add bookmark to Encrypted PDF file.");
            PDDocumentOutline outline = new PDDocumentOutline();
            document.getDocumentCatalog().setDocumentOutline(outline);
            saveBookmarkLevel((PDOutlineNode)outline, 0, items, 0);
            document.save(strFile);
            bOK = true;
        } catch (Exception ex) {
            throw ex;
        } finally {
            if (document != null)
                try {
                    document.close();
                } catch (IOException iOException) {}
        }
        return bOK;
    }

}
